package com.pactera.madp.cp.client.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpStatus;
import cn.hutool.json.JSONUtil;
import com.beta.demo.sdk.client.ApacheHttpClient;
import com.beta.demo.sdk.enums.HttpMethod;
import com.beta.demo.sdk.enums.ParamPosition;
import com.beta.demo.sdk.enums.Scheme;
import com.beta.demo.sdk.model.ApiRequest;
import com.beta.demo.sdk.model.ApiResponse;
import com.beta.demo.sdk.model.HttpClientBuilderParams;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.pactera.madp.common.core.util.R;
import com.pactera.madp.cp.api.em.beta.ChoreographyEnum;
import com.pactera.madp.cp.api.em.beta.OriginalEnum;
import com.pactera.madp.cp.api.entity.beta.WorkChoreographyEntity;
import com.pactera.madp.cp.api.entity.beta.WorkOriginalEntity;
import com.pactera.madp.cp.api.entity.sync.SyncChoreographyEntity;
import com.pactera.madp.cp.api.entity.sync.SyncOriginalEntity;
import com.pactera.madp.cp.api.vo.beta.BetaChoreographyVO;
import com.pactera.madp.cp.api.vo.beta.BetaOriginalVO;
import com.pactera.madp.cp.client.BetaApiClient;
import com.pactera.madp.cp.common.config.BetaConfig;
import com.pactera.madp.cp.config.WxProxyConfig;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletResponse;

import java.net.InetSocketAddress;
import java.net.Proxy;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static com.pactera.madp.cp.common.constant.BetaConstants.*;

/**
 * @titele 贝塔资讯API接口实现
 * @notice  2022-06-15 为保证原始数据将URL路径替换调整到同步表同步完成后执行下载并替换URL操作
 * @date 2022-06-10
 * @author Deming.Chang
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class BetaApiClientImpl extends ApacheHttpClient implements BetaApiClient {
    /**
     * 贝塔配置信息
     */
    private final BetaConfig config;
    /**
     * 代理配置信息
     */
    private final WxProxyConfig proxyConfig;
    /**
     * 序列化对象
     */
    private final ObjectMapper mapper;
    /**
     * 响应对象注入
     */
    private final HttpServletResponse servletResponse;

    /**
     * 贝塔API网关参数初始化
     */
    @PostConstruct
    public void init() {
        HttpClientBuilderParams params = new HttpClientBuilderParams();
        params.setAppKey(config.getAppKey());
        params.setAppSecret(config.getAppSecret());
        params.setScheme(Scheme.HTTPS);
        params.setHost(config.getHost());
        if(proxyConfig.isEnabled()){
            params.setProxyHostName(proxyConfig.getIp());
            params.setProxyPort(proxyConfig.getPort());
        }
        super.init(params);
    }

    @Override
    public R<BetaChoreographyVO> getChoreographyInformationList(ChoreographyEnum enums) {
        return  this.getInformation(enums.getType(), GET_CHOREOGRAPHY_INFORMATION_LIST_PATH);
    }

    @Override
    public R<BetaOriginalVO> getOriginalInformation(OriginalEnum enums) {
        return this.getInformation(enums.getType(), GET_ORIGINAL_INFORMATION_PATH);
    }

    @Override
    @SneakyThrows
    public WorkChoreographyEntity coverChoreographyInformation(SyncChoreographyEntity entity) {
        String source = mapper.writeValueAsString(entity);
        String target = this.coverAndDownload(source);
        return mapper.readValue(target, WorkChoreographyEntity.class);
    }

    @Override
    @SneakyThrows
    public WorkOriginalEntity coverOriginalInformation(SyncOriginalEntity entity) {
        String source = mapper.writeValueAsString(entity);
        String target = this.coverAndDownload(source);
        return mapper.readValue(target, WorkOriginalEntity.class);
    }

    /**
     * 根据类型和路径获取资讯信息
     * @param type  类型
     * @param path  路径
     * @return
     */
    @SneakyThrows
    private R getInformation(Integer type, String path) {
        ApiRequest request = new ApiRequest(HttpMethod.GET, path);
        request.addParam(REQUEST_PARAM_KEY, String.valueOf(type) , ParamPosition.QUERY , true);

        ApiResponse response = sendSyncRequest(request);
        this.formatLog(request, response);
        servletResponse.setStatus(response.getCode());
        if(HttpStatus.HTTP_OK != response.getCode()){
            return  R.failed(response.getHeaders().get(ERROR_KEY));
        }
        String result = new String(response.getBody(), CharsetUtil.UTF_8);
        String content = JSONUtil.getByPath(JSONUtil.parse(result), DATA_KEY).toString();
        if(StrUtil.equals(GET_CHOREOGRAPHY_INFORMATION_LIST_PATH, path)){
            return R.ok(mapper.readValue(content, BetaChoreographyVO.class));
        }else if (StrUtil.equals(GET_ORIGINAL_INFORMATION_PATH, path)){
            return R.ok(mapper.readValue(content, BetaOriginalVO.class));
        }
        return R.ok();
    }

    /**
     * 格式化输出日志
     * @param request   阿里云网关请求
     * @param response  阿里云网关响应
     */
    private void formatLog(ApiRequest request, ApiResponse response){
        if(log.isInfoEnabled()){
            String url = request.getUrl();
            if(StrUtil.startWith(url, Scheme.HTTPS.getValue())){
                url =  StrUtil.replace(url, Scheme.HTTPS.getValue(), Scheme.HTTPS.getValue().toLowerCase());
            } else if(StrUtil.startWith(url, Scheme.HTTP.getValue())){
                url =  StrUtil.replace(url, Scheme.HTTP.getValue(), Scheme.HTTP.getValue().toLowerCase());
            }
            log.info("[Madp] Request (beta api gateway):\n\t{} {} {}", request.getMethod(), url, request.getScheme());
            log.info("[Madp] Response (beta api gateway): Status = {}", response.getCode());
        }
    }

    /**
     * 处理资讯正文中的文件资源（如：图片、音频等）
     * 具体处理过程如下：
     * - 1、正则匹配获取文件资源URL
     * - 2、获取文件资源后缀
     * - 3、根据文件资源后缀构建存储路径(根据文件后缀分目录存储)
     * - 4、如果不是图片资源，则需构建文件名(音频文件名非常短，根据资讯ID命名)
     * - 5、根据步骤1获取的文件资源URL下载文件资源到共享存储
     * - 6、替换正文中的文件资源路径为共享存储路径
     * - 7、返回处理后的资讯正文
     * @param content   待处理的资讯正文
     * @return          处理后的资讯正文
     */
    private String coverAndDownload(String content) {
        String target = content;
        // 1.0 正则匹配获取文件资源URL
        Pattern pattern = Pattern.compile(URL_REGEX, Pattern.CASE_INSENSITIVE);
        Matcher urlMatcher = pattern.matcher(content);
        while (urlMatcher.find()) {
            String url = content.substring(urlMatcher.start(0), urlMatcher.end(0));
            if(StrUtil.endWith(url, StrUtil.BACKSLASH)){
                url = StrUtil.sub(url, 0, url.length() - 1);
            }
            // 2.0 获取文件资源后缀
            String extension = FilenameUtils.getExtension(url);
            // 3.0 根据文件资源后缀构建存储路径(根据文件后缀分目录存储)
            String downloadDir = config.getDownloadDir();
            String filename = null, newsKey = null;
            if(StrUtil.isNotBlank(extension)){
                downloadDir += (StrUtil.SLASH + extension);
                // 4.0 如果不是图片资源，则需构建文件名(音频文件名非常短，根据资讯ID命名)
                newsKey = JSONUtil.getByPath(JSONUtil.parse(content), NEWS_KEY).toString();
                if(!CollUtil.contains(config.getImageExtension(), extension.toLowerCase())){
                    filename = newsKey + StrUtil.DOT + extension;
                }
            }
            // 判断是否开启下载
            // - 只有配置了开启下载才能下载
            // - 只有文件存在扩展名才下载
            // - 只有不包含配置了忽略下载的后缀文件才下载
            if (config.getDownloadEnable()
                    && StrUtil.isNotBlank(extension)
                    && !CollUtil.contains(config.getIgnoreExtension(), extension.toLowerCase())) {
                // 5.0 根据步骤1获取的文件资源URL下载文件资源到共享存储
                filename = this.download(url, downloadDir, filename);
                // 6.0 替换正文中的文件资源路径为共享存储路径
                if (StrUtil.isNotBlank(filename)) {
                    String replacement = config.getMfsDir() + StrUtil.SLASH + extension + StrUtil.SLASH + filename;
                    target = StrUtil.replace(target, url, replacement);
                }
            }
        }
        // 7.0 返回处理后的资讯正文
        return target;
    }

    /**
     * 资讯内容（素材资源下载）
     * @param url           素材资源远程URL
     * @param downloadDir   下载路径
     * @param filename      素材资源文件名
     * @return              返回素材资源文件名
     */
    private String download(String url, String downloadDir, String filename){
        try{
            HttpRequest request = HttpRequest.get(url);
            if (proxyConfig.isEnabled()) {
                Proxy proxy = new Proxy(Proxy.Type.HTTP,
                        new InetSocketAddress(proxyConfig.getIp(),
                                proxyConfig.getPort()));
                request.setProxy(proxy);
            }
            HttpResponse response = request.executeAsync();
            if(StrUtil.isBlank(filename)){
                String disposition = response.header(HttpHeaders.CONTENT_DISPOSITION);
                filename = StrUtil.subSuf(disposition, CONTENT_DISPOSITION_PREFIX.length());
                if(StrUtil.isBlank(filename)){
                    filename = FileUtil.getName(url);
                }
            }
            response.writeBody(FileUtil.file(downloadDir + StrUtil.SLASH + filename));
        }catch (Exception e){
            log.warn("[Madp] 下载地址：{}", url);
            log.warn("[Madp] 未开启域名白名单，无法下载，：{}", e.getMessage());
        }
        return filename;
    }

}
